package controller;

import entity.FileSystem;
import org.csource.common.MyException;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/file")
public class FileAction {

    /**
     * 注入由spring生成的具有分布式特性的redisson单例，解决分布式环境下多进程并发的安全问题
     */
    @Autowired
    private Redisson redisson;

    /**
     * 注入由spring生成的redis工具类，是单例
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 往spring注册一个Redisson的类，通过IOC的动态代理机制生成一个Redisson的连接实例
     * 这里使用单台redis服务器
     * @return
     */
    @Bean
    public Redisson redisson(){
        Config config = new Config();
        // 使用单个redis服务器
        config.useSingleServer().setAddress("redis://172.16.249.130:6379").setDatabase(0);
        //config.useClusterServers().addNodeAddress("redis://172.16.249.130:6379","redis://172.16.249.131:6379","redis://172.16.249.132:6379").setScanInterval(2000);
        return (Redisson)Redisson.create(config);
    }

    /**
     * 上传文件的流程：
     * 1.先把用户提交的文件保存到本地web服务器上
     * 2.再从web服务器上传文件到分布式文件系统FastDFS上
     * @param request
     * @return
     * @throws IOException
     * @throws MyException
     */
    @RequestMapping("/upload")
    public FileSystem upload(MultipartHttpServletRequest request) throws IOException, MyException {
        //使用redis限制正在上传文件的数量为1，即tracker和storage服务器只能同时处理一个上传文件的请求
        String uploadQty = "fileUpload";
        //通过redisson获取分布式锁
        RLock rLock = redisson.getLock(uploadQty);
        //通过redisson上锁，初始过期时间为30秒
        rLock.lock(30, TimeUnit.SECONDS);
        FileSystem fileSystem = new FileSystem();
//        int uploadCount = Integer.parseInt(stringRedisTemplate.opsForValue().get("uploadCount"));

        try {
            //把文件保存到web服务器
            //获取上传文件的Java对象
            MultipartFile file = request.getFile("fname");
            //从文件对象中获取文件的原始名称
            String oFileName = file.getOriginalFilename();
            //通过字符串截取的方式，从文件原始名称中获取文件的后缀
            String extName = oFileName.substring(oFileName.lastIndexOf(".") );
            //为了避免文件因为同名而覆盖，生成全新的文件名
            String nFileName = UUID.randomUUID().toString() + extName;
            //创建web服务器保存文件的目录
            File toSaveFile = new File("/Users/ZiyangZhang/Desktop/CS/java/upload/" + nFileName);
            //将路径转换为文件
            file.transferTo(toSaveFile);
            //获取文件在web服务器的绝对路径
            String newFilePath = toSaveFile.getAbsolutePath();

            //从web服务器上传给Fastdfs
            //初始化fastdfs客户端连接参数
            ClientGlobal.initByProperties("config/fastdfs-client.properties");
            TrackerClient trackerClient = new TrackerClient();
            //客户端尝试连接tracker服务器并返回可用的storage服务器链接
            TrackerServer trackerServer = trackerClient.getConnection();
            StorageServer storageServer = null;
            //客户端获取连接到storage服务器端
            StorageClient1 client1 = new StorageClient1(trackerServer, storageServer);
            //用数组包装数据
            NameValuePair[] list = new NameValuePair[1];
            //将文件名和文件放入数组中
            list[0] = new NameValuePair("filename", oFileName);
            //web服务器端的文件上传到storage服务器端上保存
            String fileId = client1.upload_file1(newFilePath, extName, list);
            //关闭客户端与tracker端的连接
            trackerServer.close();

            //封装fileSystem数据对象到返回体中
            fileSystem.setFileId(fileId);
            fileSystem.setFileName(oFileName);
            fileSystem.setFilePath(fileId);

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //无论是否执行成功，释放当前的锁给其他进程使用
            rLock.unlock();
        }
        //返回文件对象给前端页面
        return fileSystem;
    }
}
